// baltzo_datafileloader.t.cpp                                        -*-C++-*-
#include <baltzo_datafileloader.h>

#include <baltzo_zoneinfo.h>
#include <baltzo_errorcode.h>

#include <bdls_filesystemutil.h>
#include <bdls_pathutil.h>

#include <bsla_maybeunused.h>

#include <bslmt_threadutil.h>

#include <bslma_default.h>
#include <bslma_defaultallocatorguard.h>
#include <bslma_testallocator.h>

#include <bslmf_assert.h>
#include <bslmf_usesallocator.h>

#include <bsls_assert.h>
#include <bsls_asserttest.h>
#include <bsls_platform.h>

#include <bsl_cstdlib.h>
#include <bsl_cstring.h>
#include <bsl_fstream.h>
#include <bsl_iostream.h>
#include <bsl_ostream.h> // for 'operator<<'

using namespace BloombergLP;
using namespace std;

// ============================================================================
//                              TEST PLAN
// ----------------------------------------------------------------------------
//                              Overview
//                              --------
// The component under test implements a mechanism for reading a Zoneinfo
// binary data file.  Each method of the component will be tested with
// different configurations for root path.
// ----------------------------------------------------------------------------
// CLASS METHODS
// [ 3] bool isPlausibleZoneinfoRootPath(const char *path);
//
// CREATORS
// [ 2] baltzo::DataFileLoader();
// [ 2] baltzo::DataFileLoader(const allocator_type& a);
// [ 2] ~baltzo::DataFileLoader();
//
// MANIPULATORS
// [ 6] int loadTimeZone(Zoneinfo *result, const char *timeZoneId);
// [ 2] void configureRootPath(const char *path);
// [ 3] int configureRootPathIfPlausible(const char *path);
//
// ACCESSORS
// [ 5] int loadTimeZoneFilePath(bsl::string *r, const char  *id) const;
// [ 4] const bsl::string& rootPath() const;
// [ 4] bool isRootPathPlausible() const;
//
// [ 2] allocator_type get_allocator() const;
// ----------------------------------------------------------------------------
// [ 1] BREATHING TEST
// ============================================================================

// ============================================================================
//                      STANDARD BDE ASSERT TEST MACRO
// ----------------------------------------------------------------------------
static int testStatus = 0;
static void aSsErT(int c, const char *s, int i)
{
    if (c) {
        cout << "Error " << __FILE__ << "(" << i << "): " << s
             << "    (failed)" << endl;
        if (testStatus >= 0 && testStatus <= 100) ++testStatus;
    }
}
#define ASSERT(X) { aSsErT(!(X), #X, __LINE__); }

// ============================================================================
//                   STANDARD BDE LOOP-ASSERT TEST MACROS
// ----------------------------------------------------------------------------

#define LOOP_ASSERT(I,X) { \
    if (!(X)) { cout << #I << ": " << I << "\n"; aSsErT(1, #X, __LINE__);}}

#define LOOP2_ASSERT(I,J,X) { \
    if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " \
              << J << "\n"; aSsErT(1, #X, __LINE__); } }

#define LOOP3_ASSERT(I,J,K,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" \
              << #K << ": " << K << "\n"; aSsErT(1, #X, __LINE__); } }

#define LOOP4_ASSERT(I,J,K,L,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\n"; \
       aSsErT(1, #X, __LINE__); } }

#define LOOP5_ASSERT(I,J,K,L,M,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\t" << \
       #M << ": " << M << "\n"; \
       aSsErT(1, #X, __LINE__); } }

#define LOOP6_ASSERT(I,J,K,L,M,N,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\t" << \
       #M << ": " << M << "\t" << #N << ": " << N << "\n"; \
       aSsErT(1, #X, __LINE__); } }

// ============================================================================
//                     SEMI-STANDARD TEST OUTPUT MACROS
// ----------------------------------------------------------------------------
#define P(X) cout << #X " = " << (X) << endl; // Print identifier and value.
#define Q(X) cout << "<| " #X " |>" << endl;  // Quote identifier literally.
#define P_(X) cout << #X " = " << (X) << ", "<< flush; // P(X) without '\n'
#define T_  cout << "\t" << flush;          // Print a tab (w/o newline)
#define L_ __LINE__                           // current Line number

// ============================================================================
//                     NEGATIVE-TEST MACRO ABBREVIATIONS
// ----------------------------------------------------------------------------

#define ASSERT_FAIL(expr) BSLS_ASSERTTEST_ASSERT_FAIL(expr)
#define ASSERT_PASS(expr) BSLS_ASSERTTEST_ASSERT_PASS(expr)

// ============================================================================
//                      CONVENIENCE MACROS
// ----------------------------------------------------------------------------

#define ALLOC_OF(EXPR) (EXPR).get_allocator().mechanism()

// ============================================================================
//                   GLOBAL TYPEDEFS/CONSTANTS FOR TESTING
// ----------------------------------------------------------------------------

typedef baltzo::DataFileLoader Obj;
typedef Obj::allocator_type    AllocType; // Test 'allocator_type' exists

// ============================================================================
//                                TYPE TRAITS
// ----------------------------------------------------------------------------

BSLMF_ASSERT(bslma::UsesBslmaAllocator<Obj>::value);
BSLMF_ASSERT((bsl::uses_allocator<Obj, bsl::allocator<char> >::value));

// ============================================================================
//                   GLOBAL TEST DATA
// ----------------------------------------------------------------------------

static const char *INVALID_PATH = "! INVALID_FILE_PATH !";

#define TEST_DIRECTORY_NAME "testDirectory"
static const char *TEST_DIRECTORY = TEST_DIRECTORY_NAME;

#ifdef BSLS_PLATFORM_OS_WINDOWS
static const char *TEST_GMT_FILE = TEST_DIRECTORY_NAME "\\GMT";
static const char *AMERICA_NEW_YORK_FILE
                                   = TEST_DIRECTORY_NAME "\\America\\New_York";
#else
static const char *TEST_GMT_FILE = TEST_DIRECTORY_NAME "/GMT";
static const char *AMERICA_NEW_YORK_FILE =
                                   TEST_DIRECTORY_NAME "/America/New_York";
#endif

static const char *AMERICA_NEW_YORK_ID = "America/New_York";

#define SUFFICIENTLY_LONG_STRING "123456789012345678901234567890123"

// ============================================================================
//                       GLOBAL FUNCTIONS FOR TESTING
// ----------------------------------------------------------------------------

BSLA_MAYBE_UNUSED static const unsigned char ASIA_BANGKOK_DATA[] = {
    0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0xa2, 0x6a, 0x67, 0xc4,
    0x01, 0x00, 0x00, 0x5e, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00,
    0x04, 0x42, 0x4d, 0x54, 0x00, 0x49, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xff, 0xff,
    0xff, 0x56, 0xb6, 0x85, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xa2, 0x6a, 0x67,
    0xc4, 0x01, 0x02, 0x00, 0x00, 0x5e, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x5e,
    0x3c, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x08, 0x4c, 0x4d, 0x54,
    0x00, 0x42, 0x4d, 0x54, 0x00, 0x49, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x0a, 0x49, 0x43, 0x54, 0x2d, 0x37, 0x0a
};

static const unsigned char AMERICA_NEW_YORK_DATA[] = {
    0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x9e, 0xa6, 0x1e, 0x70,
    0x9f, 0xba, 0xeb, 0x60, 0xa0, 0x86, 0x00, 0x70, 0xa1, 0x9a, 0xcd, 0x60,
    0xa2, 0x65, 0xe2, 0x70, 0xa3, 0x83, 0xe9, 0xe0, 0xa4, 0x6a, 0xae, 0x70,
    0xa5, 0x35, 0xa7, 0x60, 0xa6, 0x53, 0xca, 0xf0, 0xa7, 0x15, 0x89, 0x60,
    0xa8, 0x33, 0xac, 0xf0, 0xa8, 0xfe, 0xa5, 0xe0, 0xaa, 0x13, 0x8e, 0xf0,
    0xaa, 0xde, 0x87, 0xe0, 0xab, 0xf3, 0x70, 0xf0, 0xac, 0xbe, 0x69, 0xe0,
    0xad, 0xd3, 0x52, 0xf0, 0xae, 0x9e, 0x4b, 0xe0, 0xaf, 0xb3, 0x34, 0xf0,
    0xb0, 0x7e, 0x2d, 0xe0, 0xb1, 0x9c, 0x51, 0x70, 0xb2, 0x67, 0x4a, 0x60,
    0xb3, 0x7c, 0x33, 0x70, 0xb4, 0x47, 0x2c, 0x60, 0xb5, 0x5c, 0x15, 0x70,
    0xb6, 0x27, 0x0e, 0x60, 0xb7, 0x3b, 0xf7, 0x70, 0xb8, 0x06, 0xf0, 0x60,
    0xb9, 0x1b, 0xd9, 0x70, 0xb9, 0xe6, 0xd2, 0x60, 0xbb, 0x04, 0xf5, 0xf0,
    0xbb, 0xc6, 0xb4, 0x60, 0xbc, 0xe4, 0xd7, 0xf0, 0xbd, 0xaf, 0xd0, 0xe0,
    0xbe, 0xc4, 0xb9, 0xf0, 0xbf, 0x8f, 0xb2, 0xe0, 0xc0, 0xa4, 0x9b, 0xf0,
    0xc1, 0x6f, 0x94, 0xe0, 0xc2, 0x84, 0x7d, 0xf0, 0xc3, 0x4f, 0x76, 0xe0,
    0xc4, 0x64, 0x5f, 0xf0, 0xc5, 0x2f, 0x58, 0xe0, 0xc6, 0x4d, 0x7c, 0x70,
    0xc7, 0x0f, 0x3a, 0xe0, 0xc8, 0x2d, 0x5e, 0x70, 0xc8, 0xf8, 0x57, 0x60,
    0xca, 0x0d, 0x40, 0x70, 0xca, 0xd8, 0x39, 0x60, 0xcb, 0x88, 0xf0, 0x70,
    0xd2, 0x23, 0xf4, 0x70, 0xd2, 0x60, 0xfb, 0xe0, 0xd3, 0x75, 0xe4, 0xf0,
    0xd4, 0x40, 0xdd, 0xe0, 0xd5, 0x55, 0xc6, 0xf0, 0xd6, 0x20, 0xbf, 0xe0,
    0xd7, 0x35, 0xa8, 0xf0, 0xd8, 0x00, 0xa1, 0xe0, 0xd9, 0x15, 0x8a, 0xf0,
    0xd9, 0xe0, 0x83, 0xe0, 0xda, 0xfe, 0xa7, 0x70, 0xdb, 0xc0, 0x65, 0xe0,
    0xdc, 0xde, 0x89, 0x70, 0xdd, 0xa9, 0x82, 0x60, 0xde, 0xbe, 0x6b, 0x70,
    0xdf, 0x89, 0x64, 0x60, 0xe0, 0x9e, 0x4d, 0x70, 0xe1, 0x69, 0x46, 0x60,
    0xe2, 0x7e, 0x2f, 0x70, 0xe3, 0x49, 0x28, 0x60, 0xe4, 0x5e, 0x11, 0x70,
    0xe5, 0x57, 0x2e, 0xe0, 0xe6, 0x47, 0x2d, 0xf0, 0xe7, 0x37, 0x10, 0xe0,
    0xe8, 0x27, 0x0f, 0xf0, 0xe9, 0x16, 0xf2, 0xe0, 0xea, 0x06, 0xf1, 0xf0,
    0xea, 0xf6, 0xd4, 0xe0, 0xeb, 0xe6, 0xd3, 0xf0, 0xec, 0xd6, 0xb6, 0xe0,
    0xed, 0xc6, 0xb5, 0xf0, 0xee, 0xbf, 0xd3, 0x60, 0xef, 0xaf, 0xd2, 0x70,
    0xf0, 0x9f, 0xb5, 0x60, 0xf1, 0x8f, 0xb4, 0x70, 0xf2, 0x7f, 0x97, 0x60,
    0xf3, 0x6f, 0x96, 0x70, 0xf4, 0x5f, 0x79, 0x60, 0xf5, 0x4f, 0x78, 0x70,
    0xf6, 0x3f, 0x5b, 0x60, 0xf7, 0x2f, 0x5a, 0x70, 0xf8, 0x28, 0x77, 0xe0,
    0xf9, 0x0f, 0x3c, 0x70, 0xfa, 0x08, 0x59, 0xe0, 0xfa, 0xf8, 0x58, 0xf0,
    0xfb, 0xe8, 0x3b, 0xe0, 0xfc, 0xd8, 0x3a, 0xf0, 0xfd, 0xc8, 0x1d, 0xe0,
    0xfe, 0xb8, 0x1c, 0xf0, 0xff, 0xa7, 0xff, 0xe0, 0x00, 0x97, 0xfe, 0xf0,
    0x01, 0x87, 0xe1, 0xe0, 0x02, 0x77, 0xe0, 0xf0, 0x03, 0x70, 0xfe, 0x60,
    0x04, 0x60, 0xfd, 0x70, 0x05, 0x50, 0xe0, 0x60, 0x06, 0x40, 0xdf, 0x70,
    0x07, 0x30, 0xc2, 0x60, 0x07, 0x8d, 0x19, 0x70, 0x09, 0x10, 0xa4, 0x60,
    0x09, 0xad, 0x94, 0xf0, 0x0a, 0xf0, 0x86, 0x60, 0x0b, 0xe0, 0x85, 0x70,
    0x0c, 0xd9, 0xa2, 0xe0, 0x0d, 0xc0, 0x67, 0x70, 0x0e, 0xb9, 0x84, 0xe0,
    0x0f, 0xa9, 0x83, 0xf0, 0x10, 0x99, 0x66, 0xe0, 0x11, 0x89, 0x65, 0xf0,
    0x12, 0x79, 0x48, 0xe0, 0x13, 0x69, 0x47, 0xf0, 0x14, 0x59, 0x2a, 0xe0,
    0x15, 0x49, 0x29, 0xf0, 0x16, 0x39, 0x0c, 0xe0, 0x17, 0x29, 0x0b, 0xf0,
    0x18, 0x22, 0x29, 0x60, 0x19, 0x08, 0xed, 0xf0, 0x1a, 0x02, 0x0b, 0x60,
    0x1a, 0xf2, 0x0a, 0x70, 0x1b, 0xe1, 0xed, 0x60, 0x1c, 0xd1, 0xec, 0x70,
    0x1d, 0xc1, 0xcf, 0x60, 0x1e, 0xb1, 0xce, 0x70, 0x1f, 0xa1, 0xb1, 0x60,
    0x20, 0x76, 0x00, 0xf0, 0x21, 0x81, 0x93, 0x60, 0x22, 0x55, 0xe2, 0xf0,
    0x23, 0x6a, 0xaf, 0xe0, 0x24, 0x35, 0xc4, 0xf0, 0x25, 0x4a, 0x91, 0xe0,
    0x26, 0x15, 0xa6, 0xf0, 0x27, 0x2a, 0x73, 0xe0, 0x27, 0xfe, 0xc3, 0x70,
    0x29, 0x0a, 0x55, 0xe0, 0x29, 0xde, 0xa5, 0x70, 0x2a, 0xea, 0x37, 0xe0,
    0x2b, 0xbe, 0x87, 0x70, 0x2c, 0xd3, 0x54, 0x60, 0x2d, 0x9e, 0x69, 0x70,
    0x2e, 0xb3, 0x36, 0x60, 0x2f, 0x7e, 0x4b, 0x70, 0x30, 0x93, 0x18, 0x60,
    0x31, 0x67, 0x67, 0xf0, 0x32, 0x72, 0xfa, 0x60, 0x33, 0x47, 0x49, 0xf0,
    0x34, 0x52, 0xdc, 0x60, 0x35, 0x27, 0x2b, 0xf0, 0x36, 0x32, 0xbe, 0x60,
    0x37, 0x07, 0x0d, 0xf0, 0x38, 0x1b, 0xda, 0xe0, 0x38, 0xe6, 0xef, 0xf0,
    0x39, 0xfb, 0xbc, 0xe0, 0x3a, 0xc6, 0xd1, 0xf0, 0x3b, 0xdb, 0x9e, 0xe0,
    0x3c, 0xaf, 0xee, 0x70, 0x3d, 0xbb, 0x80, 0xe0, 0x3e, 0x8f, 0xd0, 0x70,
    0x3f, 0x9b, 0x62, 0xe0, 0x40, 0x6f, 0xb2, 0x70, 0x41, 0x84, 0x7f, 0x60,
    0x42, 0x4f, 0x94, 0x70, 0x43, 0x64, 0x61, 0x60, 0x44, 0x2f, 0x76, 0x70,
    0x45, 0x44, 0x43, 0x60, 0x45, 0xf3, 0xa8, 0xf0, 0x47, 0x2d, 0x5f, 0xe0,
    0x47, 0xd3, 0x8a, 0xf0, 0x49, 0x0d, 0x41, 0xe0, 0x49, 0xb3, 0x6c, 0xf0,
    0x4a, 0xed, 0x23, 0xe0, 0x4b, 0x9c, 0x89, 0x70, 0x4c, 0xd6, 0x40, 0x60,
    0x4d, 0x7c, 0x6b, 0x70, 0x4e, 0xb6, 0x22, 0x60, 0x4f, 0x5c, 0x4d, 0x70,
    0x50, 0x96, 0x04, 0x60, 0x51, 0x3c, 0x2f, 0x70, 0x52, 0x75, 0xe6, 0x60,
    0x53, 0x1c, 0x11, 0x70, 0x54, 0x55, 0xc8, 0x60, 0x54, 0xfb, 0xf3, 0x70,
    0x56, 0x35, 0xaa, 0x60, 0x56, 0xe5, 0x0f, 0xf0, 0x58, 0x1e, 0xc6, 0xe0,
    0x58, 0xc4, 0xf1, 0xf0, 0x59, 0xfe, 0xa8, 0xe0, 0x5a, 0xa4, 0xd3, 0xf0,
    0x5b, 0xde, 0x8a, 0xe0, 0x5c, 0x84, 0xb5, 0xf0, 0x5d, 0xbe, 0x6c, 0xe0,
    0x5e, 0x64, 0x97, 0xf0, 0x5f, 0x9e, 0x4e, 0xe0, 0x60, 0x4d, 0xb4, 0x70,
    0x61, 0x87, 0x6b, 0x60, 0x62, 0x2d, 0x96, 0x70, 0x63, 0x67, 0x4d, 0x60,
    0x64, 0x0d, 0x78, 0x70, 0x65, 0x47, 0x2f, 0x60, 0x65, 0xed, 0x5a, 0x70,
    0x67, 0x27, 0x11, 0x60, 0x67, 0xcd, 0x3c, 0x70, 0x69, 0x06, 0xf3, 0x60,
    0x69, 0xad, 0x1e, 0x70, 0x6a, 0xe6, 0xd5, 0x60, 0x6b, 0x96, 0x3a, 0xf0,
    0x6c, 0xcf, 0xf1, 0xe0, 0x6d, 0x76, 0x1c, 0xf0, 0x6e, 0xaf, 0xd3, 0xe0,
    0x6f, 0x55, 0xfe, 0xf0, 0x70, 0x8f, 0xb5, 0xe0, 0x71, 0x35, 0xe0, 0xf0,
    0x72, 0x6f, 0x97, 0xe0, 0x73, 0x15, 0xc2, 0xf0, 0x74, 0x4f, 0x79, 0xe0,
    0x74, 0xfe, 0xdf, 0x70, 0x76, 0x38, 0x96, 0x60, 0x76, 0xde, 0xc1, 0x70,
    0x78, 0x18, 0x78, 0x60, 0x78, 0xbe, 0xa3, 0x70, 0x79, 0xf8, 0x5a, 0x60,
    0x7a, 0x9e, 0x85, 0x70, 0x7b, 0xd8, 0x3c, 0x60, 0x7c, 0x7e, 0x67, 0x70,
    0x7d, 0xb8, 0x1e, 0x60, 0x7e, 0x5e, 0x49, 0x70, 0x7f, 0x98, 0x00, 0x60,
    0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
    0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
    0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
    0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
    0x02, 0x03, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xff, 0xff, 0xc7, 0xc0, 0x01,
    0x00, 0xff, 0xff, 0xb9, 0xb0, 0x00, 0x04, 0xff, 0xff, 0xc7, 0xc0, 0x01,
    0x08, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x0c, 0x45, 0x44, 0x54, 0x00, 0x45,
    0x53, 0x54, 0x00, 0x45, 0x57, 0x54, 0x00, 0x45, 0x50, 0x54, 0x00, 0x00,
    0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x54, 0x5a, 0x69, 0x66, 0x32,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x05, 0x00,
    0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x03, 0xf0, 0x90, 0xff,
    0xff, 0xff, 0xff, 0x9e, 0xa6, 0x1e, 0x70, 0xff, 0xff, 0xff, 0xff, 0x9f,
    0xba, 0xeb, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x86, 0x00, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xa1, 0x9a, 0xcd, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa2,
    0x65, 0xe2, 0x70, 0xff, 0xff, 0xff, 0xff, 0xa3, 0x83, 0xe9, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xa4, 0x6a, 0xae, 0x70, 0xff, 0xff, 0xff, 0xff, 0xa5,
    0x35, 0xa7, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa6, 0x53, 0xca, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xa7, 0x15, 0x89, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa8,
    0x33, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xfe, 0xa5, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xaa, 0x13, 0x8e, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xaa,
    0xde, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xab, 0xf3, 0x70, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xac, 0xbe, 0x69, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xad,
    0xd3, 0x52, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xae, 0x9e, 0x4b, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xaf, 0xb3, 0x34, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xb0,
    0x7e, 0x2d, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x9c, 0x51, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xb2, 0x67, 0x4a, 0x60, 0xff, 0xff, 0xff, 0xff, 0xb3,
    0x7c, 0x33, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb4, 0x47, 0x2c, 0x60, 0xff,
    0xff, 0xff, 0xff, 0xb5, 0x5c, 0x15, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb6,
    0x27, 0x0e, 0x60, 0xff, 0xff, 0xff, 0xff, 0xb7, 0x3b, 0xf7, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xb8, 0x06, 0xf0, 0x60, 0xff, 0xff, 0xff, 0xff, 0xb9,
    0x1b, 0xd9, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xe6, 0xd2, 0x60, 0xff,
    0xff, 0xff, 0xff, 0xbb, 0x04, 0xf5, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xbb,
    0xc6, 0xb4, 0x60, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe4, 0xd7, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xbd, 0xaf, 0xd0, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xbe,
    0xc4, 0xb9, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x8f, 0xb2, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xc0, 0xa4, 0x9b, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xc1,
    0x6f, 0x94, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x84, 0x7d, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xc3, 0x4f, 0x76, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc4,
    0x64, 0x5f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xc5, 0x2f, 0x58, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xc6, 0x4d, 0x7c, 0x70, 0xff, 0xff, 0xff, 0xff, 0xc7,
    0x0f, 0x3a, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x2d, 0x5e, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xc8, 0xf8, 0x57, 0x60, 0xff, 0xff, 0xff, 0xff, 0xca,
    0x0d, 0x40, 0x70, 0xff, 0xff, 0xff, 0xff, 0xca, 0xd8, 0x39, 0x60, 0xff,
    0xff, 0xff, 0xff, 0xcb, 0x88, 0xf0, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2,
    0x23, 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x60, 0xfb, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xd3, 0x75, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xd4,
    0x40, 0xdd, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x55, 0xc6, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xd6, 0x20, 0xbf, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd7,
    0x35, 0xa8, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xd8, 0x00, 0xa1, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xd9, 0x15, 0x8a, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xd9,
    0xe0, 0x83, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xda, 0xfe, 0xa7, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xdb, 0xc0, 0x65, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xdc,
    0xde, 0x89, 0x70, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xa9, 0x82, 0x60, 0xff,
    0xff, 0xff, 0xff, 0xde, 0xbe, 0x6b, 0x70, 0xff, 0xff, 0xff, 0xff, 0xdf,
    0x89, 0x64, 0x60, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x9e, 0x4d, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xe1, 0x69, 0x46, 0x60, 0xff, 0xff, 0xff, 0xff, 0xe2,
    0x7e, 0x2f, 0x70, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x49, 0x28, 0x60, 0xff,
    0xff, 0xff, 0xff, 0xe4, 0x5e, 0x11, 0x70, 0xff, 0xff, 0xff, 0xff, 0xe5,
    0x57, 0x2e, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x47, 0x2d, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xe7, 0x37, 0x10, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xe8,
    0x27, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x16, 0xf2, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xea, 0x06, 0xf1, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xea,
    0xf6, 0xd4, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6, 0xd3, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xec, 0xd6, 0xb6, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xed,
    0xc6, 0xb5, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xee, 0xbf, 0xd3, 0x60, 0xff,
    0xff, 0xff, 0xff, 0xef, 0xaf, 0xd2, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf0,
    0x9f, 0xb5, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, 0xb4, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xf2, 0x7f, 0x97, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf3,
    0x6f, 0x96, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x5f, 0x79, 0x60, 0xff,
    0xff, 0xff, 0xff, 0xf5, 0x4f, 0x78, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf6,
    0x3f, 0x5b, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x2f, 0x5a, 0x70, 0xff,
    0xff, 0xff, 0xff, 0xf8, 0x28, 0x77, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xf9,
    0x0f, 0x3c, 0x70, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x08, 0x59, 0xe0, 0xff,
    0xff, 0xff, 0xff, 0xfa, 0xf8, 0x58, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xfb,
    0xe8, 0x3b, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xd8, 0x3a, 0xf0, 0xff,
    0xff, 0xff, 0xff, 0xfd, 0xc8, 0x1d, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfe,
    0xb8, 0x1c, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x97, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x01,
    0x87, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x77, 0xe0, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x03, 0x70, 0xfe, 0x60, 0x00, 0x00, 0x00, 0x00, 0x04,
    0x60, 0xfd, 0x70, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0xe0, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x06, 0x40, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00, 0x07,
    0x30, 0xc2, 0x60, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d, 0x19, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x09, 0x10, 0xa4, 0x60, 0x00, 0x00, 0x00, 0x00, 0x09,
    0xad, 0x94, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x86, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x0b, 0xe0, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0c,
    0xd9, 0xa2, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc0, 0x67, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x0e, 0xb9, 0x84, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f,
    0xa9, 0x83, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x66, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x11, 0x89, 0x65, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x12,
    0x79, 0x48, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x47, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x14, 0x59, 0x2a, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x15,
    0x49, 0x29, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x0c, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x17, 0x29, 0x0b, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x18,
    0x22, 0x29, 0x60, 0x00, 0x00, 0x00, 0x00, 0x19, 0x08, 0xed, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x1a, 0x02, 0x0b, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1a,
    0xf2, 0x0a, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe1, 0xed, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x1c, 0xd1, 0xec, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1d,
    0xc1, 0xcf, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb1, 0xce, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x1f, 0xa1, 0xb1, 0x60, 0x00, 0x00, 0x00, 0x00, 0x20,
    0x76, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0x93, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x22, 0x55, 0xe2, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x23,
    0x6a, 0xaf, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xc4, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x25, 0x4a, 0x91, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x26,
    0x15, 0xa6, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2a, 0x73, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x27, 0xfe, 0xc3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x29,
    0x0a, 0x55, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x29, 0xde, 0xa5, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x2a, 0xea, 0x37, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x2b,
    0xbe, 0x87, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd3, 0x54, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x2d, 0x9e, 0x69, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2e,
    0xb3, 0x36, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7e, 0x4b, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x30, 0x93, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x31,
    0x67, 0x67, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0xfa, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x33, 0x47, 0x49, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x34,
    0x52, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x2b, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x36, 0x32, 0xbe, 0x60, 0x00, 0x00, 0x00, 0x00, 0x37,
    0x07, 0x0d, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1b, 0xda, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x38, 0xe6, 0xef, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x39,
    0xfb, 0xbc, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc6, 0xd1, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x3b, 0xdb, 0x9e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3c,
    0xaf, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0x80, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x3e, 0x8f, 0xd0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3f,
    0x9b, 0x62, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6f, 0xb2, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x41, 0x84, 0x7f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x4f, 0x94, 0x70, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x61, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x44, 0x2f, 0x76, 0x70, 0x00, 0x00, 0x00, 0x00, 0x45,
    0x44, 0x43, 0x60, 0x00, 0x00, 0x00, 0x00, 0x45, 0xf3, 0xa8, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x47, 0x2d, 0x5f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x47,
    0xd3, 0x8a, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0d, 0x41, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x49, 0xb3, 0x6c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x4a,
    0xed, 0x23, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x9c, 0x89, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x4c, 0xd6, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4d,
    0x7c, 0x6b, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb6, 0x22, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x4f, 0x5c, 0x4d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x50,
    0x96, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3c, 0x2f, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x52, 0x75, 0xe6, 0x60, 0x00, 0x00, 0x00, 0x00, 0x53,
    0x1c, 0x11, 0x70, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xc8, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x54, 0xfb, 0xf3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x56,
    0x35, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x00, 0x56, 0xe5, 0x0f, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x58, 0x1e, 0xc6, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x58,
    0xc4, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x59, 0xfe, 0xa8, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x5a, 0xa4, 0xd3, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x5b,
    0xde, 0x8a, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x84, 0xb5, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x5d, 0xbe, 0x6c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x5e,
    0x64, 0x97, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9e, 0x4e, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x60, 0x4d, 0xb4, 0x70, 0x00, 0x00, 0x00, 0x00, 0x61,
    0x87, 0x6b, 0x60, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2d, 0x96, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x63, 0x67, 0x4d, 0x60, 0x00, 0x00, 0x00, 0x00, 0x64,
    0x0d, 0x78, 0x70, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x2f, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x65, 0xed, 0x5a, 0x70, 0x00, 0x00, 0x00, 0x00, 0x67,
    0x27, 0x11, 0x60, 0x00, 0x00, 0x00, 0x00, 0x67, 0xcd, 0x3c, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x69, 0x06, 0xf3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x69,
    0xad, 0x1e, 0x70, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe6, 0xd5, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x6b, 0x96, 0x3a, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x6c,
    0xcf, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x76, 0x1c, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x6e, 0xaf, 0xd3, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x6f,
    0x55, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8f, 0xb5, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x71, 0x35, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x72,
    0x6f, 0x97, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xc2, 0xf0, 0x00,
    0x00, 0x00, 0x00, 0x74, 0x4f, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x74,
    0xfe, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, 0x96, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x76, 0xde, 0xc1, 0x70, 0x00, 0x00, 0x00, 0x00, 0x78,
    0x18, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x78, 0xbe, 0xa3, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x79, 0xf8, 0x5a, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7a,
    0x9e, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd8, 0x3c, 0x60, 0x00,
    0x00, 0x00, 0x00, 0x7c, 0x7e, 0x67, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7d,
    0xb8, 0x1e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x5e, 0x49, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x7f, 0x98, 0x00, 0x60, 0x02, 0x01, 0x02, 0x01, 0x02,
    0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
    0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
    0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
    0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
    0x02, 0x01, 0x02, 0xff, 0xff, 0xba, 0x9e, 0x00, 0x00, 0xff, 0xff, 0xc7,
    0xc0, 0x01, 0x04, 0xff, 0xff, 0xb9, 0xb0, 0x00, 0x08, 0xff, 0xff, 0xc7,
    0xc0, 0x01, 0x0c, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x10, 0x4c, 0x4d, 0x54,
    0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, 0x57, 0x54,
    0x00, 0x45, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x01, 0x0a, 0x45, 0x53, 0x54, 0x35, 0x45, 0x44, 0x54, 0x2c,
    0x4d, 0x33, 0x2e, 0x32, 0x2e, 0x30, 0x2c, 0x4d, 0x31, 0x31, 0x2e, 0x31,
    0x2e, 0x30, 0x0a
};

static void writeData(const char *fileName, const char *data, int numBytes)
{
    int rc = bdls::FilesystemUtil::createDirectories(fileName, false);

    if (rc != 0) {
        // If this test-driver is being run in parallel, its possible for two
        // instances to attempt to create the directory simultaneously, and
        // for one of them to fail.  But the directory should still exist.

        bslmt::ThreadUtil::microSleep(0,1);
        bsl::string path(fileName);
        bdls::PathUtil::popLeaf(&path);
        ASSERT(bdls::FilesystemUtil::exists(path));
    }
//..
// Then we create a file for Bangkok and write the binary time zone data to
// that file.
//..
    bsl::ofstream outputFile(fileName, bsl::ofstream::binary);
    ASSERT(outputFile.is_open());
    outputFile.write(data, numBytes);
    outputFile.close();
}

// ============================================================================
//                               MAIN PROGRAM
// ----------------------------------------------------------------------------

int main(int argc, char *argv[])
{
    int                test = argc > 1 ? atoi(argv[1]) : 0;
    int             verbose = argc > 2;
    int         veryVerbose = argc > 3;
    int     veryVeryVerbose = argc > 4;
    int veryVeryVeryVerbose = argc > 5;

    cout << "TEST " << __FILE__ << " CASE " << test << endl;

    if (!bdls::FilesystemUtil::exists(TEST_DIRECTORY)) {
        bdls::FilesystemUtil::createDirectories(TEST_DIRECTORY, true);
    }

    if (!bdls::FilesystemUtil::exists(TEST_GMT_FILE)) {
        bsl::ofstream outputFile(TEST_GMT_FILE, bsl::ofstream::binary);
        ASSERT(outputFile.is_open());
        outputFile.close();
    }

    if (!bdls::FilesystemUtil::exists(AMERICA_NEW_YORK_FILE)) {
        writeData(AMERICA_NEW_YORK_FILE,
                  reinterpret_cast<const char  *>(AMERICA_NEW_YORK_DATA),
                  sizeof(AMERICA_NEW_YORK_DATA));
    }

    // Remove GMT file for usage example.

    if (bdls::FilesystemUtil::exists("test/GMT")) {
        bdls::FilesystemUtil::remove("test/GMT");
    }

    bslma::TestAllocator allocator; bslma::TestAllocator *Z = &allocator;
    static bslma::TestAllocator defaultAllocator;

    bslma::DefaultAllocatorGuard guard(&defaultAllocator);
    if (veryVeryVerbose) {
        defaultAllocator.setVerbose(true);
    }

    switch (test) { case 0:
      case 7: {
        // --------------------------------------------------------------------
        // TESTING USAGE EXAMPLE
        //
        // Concerns:
        //   The usage example provided in the component header file must
        //   compile, link, and run on all platforms as shown.
        //
        // Plan:
        //   Incorporate usage example from header into driver, remove leading
        //   comment characters, and replace 'assert' with 'ASSERT'.
        //
        // Testing:
        //   USAGE EXAMPLE
        // --------------------------------------------------------------------

        if (verbose) cout << "\nTesting Usage Example"
                          << "\n=====================" << endl;

///Usage
///-----
// The following example illustrates how to use a 'baltzo::DataFileLoader' to
// load the Zoneinfo time zone data for a time zone.
//
///Prologue: Create a Example Data File
/// - - - - - - - - - - - - - - - - - -
// We need to create one time zone data file on which to operate in the
// remainder of the example.  In practice, clients should *not* generate data
// files in this manner.  Data files are typically created using the 'zic'
// compiler -- a publicly available tool provided as part of the standard
// Zoneinfo distribution (see 'http://www.twinsun.com/tz/tz-link.htm') -- and
// deployed in a standard directory location (see
// 'baltzo_defaultzoneinfocache').
//
// First we define static binary data for "Asia/Bangkok" (chosen because it is
// relatively small):
//..
    const unsigned char ASIA_BANGKOK_DATA[] = {
      0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
      0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
      0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0xa2, 0x6a, 0x67, 0xc4,
      0x01, 0x00, 0x00, 0x5e, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00,
      0x04, 0x42, 0x4d, 0x54, 0x00, 0x49, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xff, 0xff,
      0xff, 0x56, 0xb6, 0x85, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xa2, 0x6a, 0x67,
      0xc4, 0x01, 0x02, 0x00, 0x00, 0x5e, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x5e,
      0x3c, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x08, 0x4c, 0x4d, 0x54,
      0x00, 0x42, 0x4d, 0x54, 0x00, 0x49, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x0a, 0x49, 0x43, 0x54, 0x2d, 0x37, 0x0a
    };
//..
// Then we create a testing sub-directory "test/Asia" that will hold the
// data file for Bangkok.  Note that "Asia/Bangkok" is the Olson time zone
// identifier for Bangkok and "Asia/Bangkok" also serves as a path (relative
// to our "./test" sub-directory) to that data file.
//..
#ifdef BSLS_PLATFORM_OS_WINDOWS
    const char *TIME_ZONE_DIRECTORY = "test\\Asia";
    const char *TIME_ZONE_FILE      = "test\\Asia\\Bangkok";
#else
    const char *TIME_ZONE_DIRECTORY = "test/Asia";
    const char *TIME_ZONE_FILE      = "test/Asia/Bangkok";
#endif
    int rc =
            bdls::FilesystemUtil::createDirectories(TIME_ZONE_DIRECTORY, true);
    ASSERT(0 == rc);
//..
// Finally we create a file for Bangkok and write the binary time zone data to
// that file.
//..
    bsl::ofstream outputFile(TIME_ZONE_FILE, bsl::ofstream::binary);
    ASSERT(outputFile.is_open());
    outputFile.write(reinterpret_cast<const char *>(ASIA_BANGKOK_DATA),
                     sizeof(ASIA_BANGKOK_DATA));
    outputFile.close();
//..
//
///Example 1: Using a 'baltzo::DataFileLoader' to Load a Zoneinfo File
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example we demonstrate how to use a 'baltzo::DataFileLoader' to load
// a time zone data file into a 'baltzo::Zoneinfo' object.  First we create
// a 'baltzo::DataFileLoader' object, and configure it with the relative path
// "test" which we created in the prologue example.
//..
    baltzo::DataFileLoader loader;
    loader.configureRootPath("test");
//..
// Note that 'isRootPathPlausible' will return 'false' for the relative path
// "test" since it does not contain data for the time zone "GMT" so it is
// (correctly) assumed that "test" does not contain a (reasonably complete)
// set of time zone data:
//..
    ASSERT(!loader.isRootPathPlausible());
//..
// Next we use the 'dataFilePath' method to verify that 'loader' will
// correctly locate the test data file we've created:
//..
    const char *BANGKOK_ID = "Asia/Bangkok";
    bsl::string bangkokDataPath;
    rc = loader.loadTimeZoneFilePath(&bangkokDataPath, BANGKOK_ID);

    ASSERT(0              == rc);
    ASSERT(TIME_ZONE_FILE == bangkokDataPath);
//..
// Then we create a 'baltzo::Zoneinfo' and load it using 'loader':
//..
    baltzo::Zoneinfo timeZone;
    rc = loader.loadTimeZone(&timeZone, BANGKOK_ID);
    ASSERT(0 == rc);
//..
// Finally we verify several properties of the timezone: (1) That its
// identifier is "Asia/Bangkok", and (2) it contains two transitions referring
// to three local time descriptors, "LMT" (Local Mean Time), "BMT" (Bangkok
// Mean Time), and "ICT" (Indochina Time):
//..
    ASSERT(BANGKOK_ID == timeZone.identifier());
    baltzo::Zoneinfo::TransitionConstIterator iterator =
                                                   timeZone.beginTransitions();
    ASSERT("LMT" == iterator->descriptor().description());
    ++iterator;
    ASSERT("BMT" == iterator->descriptor().description());
    ++iterator;
    ASSERT("ICT" == iterator->descriptor().description());
    ++iterator;
    ASSERT(timeZone.endTransitions() == iterator);
//..
    if (verbose) {
        timeZone.print(bsl::cout, 1, 3);
    }

    bdls::FilesystemUtil::remove("test", true);
                                          // TIME_ZONE_DIRECTORY/.. i.e. "test"

      } break;
      case 6: {
        // --------------------------------------------------------------------
        // TESTING 'loadTimeZone'
        //
        // Concerns:
        //: 1 'loadTimeZone' correctly loads time-zone information when a valid
        //:   time zone identifier is specified.
        //:
        //: 2 'loadTimeZone' returns 'k_UNSUPPORTED_ID' if the specified time
        //:   zone identifier is invalid.
        //:
        //: 3 'loadTimeZone' returns a non-zero value different from
        //:   'k_UNSUPPORTED_ID' on error reading the specified time-zone file.
        //
        // Plan:
        //: 1 Test that 'loadTimeZone' returns time-zone information when give
        //:   a valid time zone identifier.
        //:
        //: 2 Test that 'loadTimeZone' returns 'k_UNSUPPORTED_ID' when
        //:   'rootPath' is a plausible directory, but the time zone identifier
        //:   is invalid.
        //:
        //: 3 Test that 'loadTimeZone' returns a non-zero return code other
        //:   than 'k_UNSUPPORTED_ID' if 'rootPath' is not plausible.
        //
        // Testing:
        //   int loadTimeZone(Zoneinfo *result, const char *timeZoneId);
        // --------------------------------------------------------------------

        if (verbose) cout << endl
                          << "TESTING 'loadTimeZone'" << endl
                          << "======================" << endl;

        if (verbose) cout << "\nSetup plausible directory" << endl;

        ASSERT(true == Obj::isPlausibleZoneinfoRootPath(TEST_DIRECTORY));

        if (verbose) cout << "\nTest non-plausible directory" << endl;
        {
            Obj mX;
            baltzo::Zoneinfo timeZone;

            mX.configureRootPath("NotPlausibleDirectory");

            int rc = mX.loadTimeZone(&timeZone, "A");
            ASSERT(0 != rc);
            ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID != rc);
        }

        if (verbose) cout << "\nTest with plausible directory" << endl;
        {
            Obj mX;
            baltzo::Zoneinfo timeZone;

            ASSERT(0 == mX.configureRootPathIfPlausible(TEST_DIRECTORY));

            if (verbose) cout << "\n\tTest invalid identifier" << endl;

            ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
                                              mX.loadTimeZone(&timeZone, "/"));

            if (verbose) cout << "\n\tTest non-existent identifier" << endl;

            ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
                        mX.loadTimeZone(&timeZone, "Non/Existent/Identifier"));

            ASSERT(0 == mX.loadTimeZone(&timeZone, AMERICA_NEW_YORK_ID));

            ASSERT(AMERICA_NEW_YORK_ID == timeZone.identifier());
        }

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
        ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
               baltzo::ErrorCode::k_UNSUPPORTED_ID);
#endif  // BDE_OMIT_INTERNAL_DEPRECATED

      } break;
      case 5: {
        // --------------------------------------------------------------------
        // TESTING 'loadTimeZoneFilePath'
        //
        // Concerns:
        //: 1 All invalid characters cause 'loadTimeZoneFilePath' to return a
        //:   non-zero value.
        //:
        //: 2 'loadTimeZoneFilePath' accepts all valid characters in the
        //:   'timeZoneId'.
        //:
        //: 3 'loadTimeZoneFilePath' returns the expected result as specified
        //:   in the contract.
        //:
        //: 4 'loadTimeZoneFilePath' accepts all current time zone identifiers.
        //
        // Plan:
        //: 1 Create a list of invalid character and test that
        //:   'loadTimeZoneFilePath' returns a non-zero value for each one of
        //:   them.
        //:
        //: 2 Test that 'loadTimeZoneFilePath' succeed when given a time zone
        //:   identifier that contains only valid character.
        //:
        //: 3 Test that 'loadTimeZoneFilePath' produce expected results at the
        //:   documented boundaries.
        //:
        //: 4 Create a list of sample time zone identifiers and test that the
        //:   the correct file path is returned.
        //
        // Testing:
        //   int loadTimeZoneFilePath(bsl::string *r, const char  *id) const;
        // --------------------------------------------------------------------

        if (verbose) cout << endl
                          << "TESTING 'loadTimeZoneFilePath'" << endl
                          << "==============================" << endl;

        if (verbose) cout << "\nTesting invalid characters." << endl;
        {
            const char *INVALID_CHAR = "`~!@#$%^&*()=,./;'[]\\<>?:\"{}|";
            const bsl::size_t NUM_INVALID_CHAR = strlen(INVALID_CHAR);

            Obj mX; const Obj& X = mX;
            mX.configureRootPath(".");

            char TZ_ID[2];
            TZ_ID[1] = '\0';

            for (bsl::size_t ti = 0; ti < NUM_INVALID_CHAR; ++ti) {
                TZ_ID[0] = INVALID_CHAR[ti];

                if (veryVerbose) { T_ P_(ti) P_(TZ_ID) };

                bsl::string result;
                LOOP2_ASSERT(ti, TZ_ID,
                             0 != X.loadTimeZoneFilePath(&result, TZ_ID));
            }
        }

        if (verbose) cout << "\nTesting valid characters." << endl;
        {
            const char *VALID_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                     "abcdefghijklmnopqrstuvwxyz"
                                     "1234567890_+-";
            const bsl::size_t NUM_VALID_CHAR = strlen(VALID_CHAR);

            Obj mX; const Obj& X = mX;
            mX.configureRootPath(".");

            char TZ_ID[2];
            TZ_ID[1] = '\0';

            for (bsl::size_t ti = 0; ti < NUM_VALID_CHAR; ++ti) {
                TZ_ID[0] = VALID_CHAR[ti];

                if (veryVerbose) { T_ P_(ti) P_(TZ_ID) };

                bsl::string result;
                LOOP2_ASSERT(ti, TZ_ID,
                             0 == X.loadTimeZoneFilePath(&result, TZ_ID));
            }
        }

        if (verbose) cout << "\nTesting '/' characters." << endl;
        {
            Obj mX; const Obj& X = mX;
            mX.configureRootPath(".");

            bsl::string result;

            ASSERT(0 != X.loadTimeZoneFilePath(&result, "/"));
            ASSERT(0 != X.loadTimeZoneFilePath(&result, "/A"));
            ASSERT(0 == X.loadTimeZoneFilePath(&result, "A/"));
            ASSERT(0 == X.loadTimeZoneFilePath(&result, "A/B"));
        }

        if (verbose) cout <<
                         "\nCreate a table of distinct object values." << endl;
        {
            static const struct {
                int         d_line;
                const char *d_root;
                const char *d_tzId;
                const char *d_result;
            } DATA [] = {

            //LINE  ROOT    TZ_ID   RESULT
            //----  ----    -----   ------

#ifdef BSLS_PLATFORM_OS_WINDOWS
            { L_,   "",     "",     "" },
            { L_,   "",     "C",    "C" },
            { L_,   "A",    "C/D",  "A\\C\\D" },
            { L_,   "A\\B", "C/D",  "A\\B\\C\\D" },
#else
            { L_,   "",     "",     "" },
            { L_,   "",     "C",    "C" },
            { L_,   "A",    "C/D",  "A/C/D" },
            { L_,   "A/B",  "C/D",  "A/B/C/D" },
#endif

            };
            const int NUM_DATA = sizeof DATA / sizeof *DATA;

            if (verbose) cout << "\nTesting with various path." << endl;
            {
                for (int ti = 0; ti < NUM_DATA; ++ti) {
                    const int          LINE   = DATA[ti].d_line;
                    const char        *ROOT   = DATA[ti].d_root;
                    const char        *TZ_ID  = DATA[ti].d_tzId;
                    const bsl::string  EXP    = DATA[ti].d_result;

                    Obj mX; const Obj& X = mX;

                    mX.configureRootPath(ROOT);

                    bsl::string result;
                    LOOP_ASSERT(LINE,
                                0 == X.loadTimeZoneFilePath(&result, TZ_ID));
                    LOOP3_ASSERT(LINE, EXP, result, EXP == result);
                }
            }
        }

        if (verbose) cout <<
                         "\nCreate a table of time zones." << endl;
        {
#define ROOT "root"
            static const struct {
                int         d_line;
                const char *d_tzId;
                const char *d_result;
            } DATA [] = {

#ifdef BSLS_PLATFORM_OS_WINDOWS
            { L_, "Africa/Cairo",           ROOT "\\Africa\\Cairo" },
            { L_, "Africa/Johannesburg",    ROOT "\\Africa\\Johannesburg" },
            { L_, "Atlantic/South_Georgia", ROOT "\\Atlantic\\South_Georgia" },
            { L_, "Indian/Christmas",       ROOT "\\Indian\\Christmas" },
            { L_, "Antarctica/South_Pole",  ROOT "\\Antarctica\\South_Pole" },
            { L_, "Asia/Tokyo",             ROOT "\\Asia\\Tokyo" },
            { L_, "Asia/Ulaanbaatar",       ROOT "\\Asia\\Ulaanbaatar" },
            { L_, "Asia/Riyadh87",          ROOT "\\Asia\\Riyadh87" },
            { L_, "Australia/Darwin",       ROOT "\\Australia\\Darwin" },
            { L_, "Pacific/Honolulu",       ROOT "\\Pacific\\Honolulu" },
            { L_, "Europe/London",          ROOT "\\Europe\\London" },
            { L_, "Europe/Isle_of_Man",     ROOT "\\Europe\\Isle_of_Man" },
            { L_, "America/New_York",       ROOT "\\America\\New_York" },
            { L_, "America/Indiana/Indianapolis",
                                   ROOT "\\America\\Indiana\\Indianapolis" },
            { L_, "America/Blanc-Sablon",   ROOT "\\America\\Blanc-Sablon" },
            { L_, "America/Port-au-Prince", ROOT "\\America\\Port-au-Prince" },
            { L_, "America/Argentina/Buenos_Aires",
                                   ROOT "\\America\\Argentina\\Buenos_Aires" },
            { L_, "EET",                    ROOT "\\EET" },
            { L_, "EST5EDT",                ROOT "\\EST5EDT" },
            { L_, "Etc/GMT-14",             ROOT "\\Etc\\GMT-14" },
            { L_, "Etc/GMT+12",             ROOT "\\Etc\\GMT+12" },
            { L_, "Etc/GMT-0",              ROOT "\\Etc\\GMT-0" },
            { L_, "Etc/GMT+0",              ROOT "\\Etc\\GMT+0" },
            { L_, "Etc/GMT0",               ROOT "\\Etc\\GMT0" },
            { L_, "Etc/UTC",                ROOT "\\Etc\\UTC" },
            { L_, "Arctic/Longyearbyen",    ROOT "\\Arctic\\Longyearbyen" },
            { L_, "UTC",                    ROOT "\\UTC" },
#else
            { L_, "Africa/Cairo",           ROOT "/Africa/Cairo" },
            { L_, "Africa/Johannesburg",    ROOT "/Africa/Johannesburg" },
            { L_, "Atlantic/South_Georgia", ROOT "/Atlantic/South_Georgia" },
            { L_, "Indian/Christmas",       ROOT "/Indian/Christmas" },
            { L_, "Antarctica/South_Pole",  ROOT "/Antarctica/South_Pole" },
            { L_, "Asia/Tokyo",             ROOT "/Asia/Tokyo" },
            { L_, "Asia/Ulaanbaatar",       ROOT "/Asia/Ulaanbaatar" },
            { L_, "Asia/Riyadh87",          ROOT "/Asia/Riyadh87" },
            { L_, "Australia/Darwin",       ROOT "/Australia/Darwin" },
            { L_, "Pacific/Honolulu",       ROOT "/Pacific/Honolulu" },
            { L_, "Europe/London",          ROOT "/Europe/London" },
            { L_, "Europe/Isle_of_Man",     ROOT "/Europe/Isle_of_Man" },
            { L_, "America/New_York",       ROOT "/America/New_York" },
            { L_, "America/Indiana/Indianapolis",
                                      ROOT "/America/Indiana/Indianapolis" },
            { L_, "America/Blanc-Sablon",   ROOT "/America/Blanc-Sablon" },
            { L_, "America/Port-au-Prince", ROOT "/America/Port-au-Prince" },
            { L_, "America/Argentina/Buenos_Aires",
                                      ROOT "/America/Argentina/Buenos_Aires" },
            { L_, "EET",                    ROOT "/EET" },
            { L_, "EST5EDT",                ROOT "/EST5EDT" },
            { L_, "Etc/GMT-14",             ROOT "/Etc/GMT-14" },
            { L_, "Etc/GMT+12",             ROOT "/Etc/GMT+12" },
            { L_, "Etc/GMT-0",              ROOT "/Etc/GMT-0" },
            { L_, "Etc/GMT+0",              ROOT "/Etc/GMT+0" },
            { L_, "Etc/GMT0",               ROOT "/Etc/GMT0" },
            { L_, "Etc/UTC",                ROOT "/Etc/UTC" },
            { L_, "Arctic/Longyearbyen",    ROOT "/Arctic/Longyearbyen" },
            { L_, "UTC",                    ROOT "/UTC" },
#endif
            };
            const int NUM_DATA = sizeof DATA / sizeof *DATA;

            if (verbose) cout <<
                        "\nTesting with various time zone identifier." << endl;
            {
                for (int ti = 0; ti < NUM_DATA; ++ti) {
                    const int          LINE   = DATA[ti].d_line;
                    const char        *TZ_ID  = DATA[ti].d_tzId;
                    const bsl::string  EXP    = DATA[ti].d_result;

                    Obj mX; const Obj& X = mX;

                    mX.configureRootPath(ROOT);

                    bsl::string result;
                    LOOP_ASSERT(LINE,
                                0 == X.loadTimeZoneFilePath(&result, TZ_ID));
                    LOOP3_ASSERT(LINE, EXP, result, EXP == result);
                }
            }
#undef ROOT
        }
      } break;
      case 4: {
        // --------------------------------------------------------------------
        // BASIC ACCESSORS
        //
        // Concerns:
        //: 1 'rootPath' returns the value of the root path stored in the
        //:   object.
        //:
        //: 2 'isRootPathPlausible' returns 'true' if the specified root path
        //:   is a valid directory and contains a file name 'GMT, and false
        //:   otherwise.
        //:
        //: 3 Each accessor is declared 'const'.
        //
        // Plan:
        //: 1 Use primary manipulators to set the root path to various values.
        //:
        //: 2 Verify that each basic accessor, invoked on a reference to the
        //:   non-modifiable object created in P1 returns the expected value.
        //
        // Testing:
        //   const bsl::string& rootPath() const;
        //   bool isRootPathPlausible() const;
        // --------------------------------------------------------------------

        if (verbose) cout << endl
                          << "BASIC ACCESSORS" << endl
                          << "===============" << endl;

        if (verbose) cout << "\nSetup plausible directory" << endl;

        ASSERT(true == Obj::isPlausibleZoneinfoRootPath(TEST_DIRECTORY));

        static const struct {
            int         d_line;
            const char *d_path;
            int         d_isPlausible;
        } DATA [] = {

        //LINE  PATH             IS_PLAUSIBLE
        //----  ----             ------------

        { L_,   "",                     false},
        { L_,   "Nonexistent",          false},
        { L_,   ".",                    false},
        { L_,   TEST_DIRECTORY,          true},

        };
        const int NUM_DATA = sizeof DATA / sizeof *DATA;

        if (verbose) cout << "\nTesting with various path." << endl;
        {
            for (int ti = 0; ti < NUM_DATA; ++ti) {
                const int   LINE         = DATA[ti].d_line;
                const char *PATH         = DATA[ti].d_path;
                const bool  IS_PLAUSIBLE = DATA[ti].d_isPlausible;

                Obj mX; const Obj& X = mX;

                if (veryVerbose) { T_ P_(PATH) P(IS_PLAUSIBLE) }

                mX.configureRootPath(PATH);

                LOOP3_ASSERT(LINE, PATH, X.rootPath(), PATH == X.rootPath());
                LOOP3_ASSERT(LINE, IS_PLAUSIBLE, X.isRootPathPlausible(),
                             IS_PLAUSIBLE == X.isRootPathPlausible());
            }
        }

      } break;
      case 3: {
        // --------------------------------------------------------------------
        // TESTING 'isPlausibleZoneinfoRootPath'
        //
        // Concerns:
        //: 1 'isPlausibleZoneinfoRootPath' returns value returns 'true' if
        //:   the specified 'path' is a valid directory and contains a file
        //:   name 'GMT, and false otherwise.
        //:
        //: 2 'configureRootPathIfPlausible' will only set the root path if the
        //:   parameter is a plausible Zoneinfo data path.
        //
        // Plan:
        //: 1 Test that 'isPlausibleZoneinfoRootPath' produce expected results
        //:   at the documented boundaries.
        //:
        //: 2 Test that 'configureRootPathIfPlausible' set the 'rootPath' and
        //:   returns 0 for a plausible path, and returns false otherwise.
        //
        // Testing:
        //   bool isPlausibleZoneinfoRootPath(const char *path);
        //   int configureRootPathIfPlausible(const char *path);
        // --------------------------------------------------------------------

        if (verbose) cout << endl
                          << "TESTING 'isPlausibleZoneinfoRootPath'" << endl
                          << "=====================================" << endl;

        {
            Obj mX;

            if (veryVerbose) cout << "\tNon-existent directory" << endl;
            {
                ASSERT(false ==
                     Obj::isPlausibleZoneinfoRootPath("NonExistentDirectory"));
                ASSERT(0 !=
                      mX.configureRootPathIfPlausible("NonExistentDirectory"));
            }

            if (veryVerbose) cout << "\tMissing GMT file" << endl;
            {
                ASSERT(false ==
                             Obj::isPlausibleZoneinfoRootPath("."));
                ASSERT(0 != mX.configureRootPathIfPlausible("."));
            }

            if (veryVerbose) cout << "\tPlausible root path" << endl;
            {
                ASSERT(true ==
                             Obj::isPlausibleZoneinfoRootPath(TEST_DIRECTORY));
                ASSERT(0 == mX.configureRootPathIfPlausible(TEST_DIRECTORY));
                ASSERT(TEST_DIRECTORY == mX.rootPath());
            }
        }
      } break;
      case 2: {
        // --------------------------------------------------------------------
        // DEFAULT CTOR & PRIMARY MANIPULATORS
        //
        // Concerns:
        //: 1 Default constructor creates an object with an invalid root path.
        //:
        //: 2 If an allocator is *not* supplied to the default constructor, the
        //:   default allocator in effect at the time of construction becomes
        //:   the object allocator for the resulting object.
        //:
        //: 3 If an allocator is supplied to the default constructor, that
        //:   allocator becomes the object allocator for the resulting object.
        //:
        //: 4 Supplying a default-constructed allocator address has the same
        //:   effect as not supplying an allocator.
        //:
        //: 5 Supplying an allocator to the default constructor has no effect
        //:   on subsequent object values.
        //:
        //: 6 Any memory allocation is from the object allocator, which is
        //:   returned by the 'get_allocator' accessor method.
        //:
        //: 7 Every object releases any allocated memory at destruction.
        //:
        //: 8 'configureRootPath' is able to set the root path to any string
        //:   value.
        //
        // Plan:
        //: 1 Default construct four objects, in turn, with different allocator
        //:   configurations:
        //:   1 without passing an allocator,
        //:   2 passing a default-constructed allocator explicitly,
        //:   3 passing the address of a test allocator distinct from the
        //:     default, and
        //:   4 passing in an allocator constructed from the address of a test
        //:     allocator distinct from the default.
        //:
        //: 2 Use the primary manipulators with boundary values and verify the
        //:   object's value.
        //:
        //: 3 Verify the correct memory allocator is used.
        //
        // Testing:
        //   baltzo::DataFileLoader();
        //   baltzo::DataFileLoader(const allocator_type& a);
        //   ~baltzo::DataFileLoader();
        //   void configureRootPath(const char *path);
        //   allocator_type get_allocator() const;
        // --------------------------------------------------------------------

        if (verbose) cout << endl
                          << "DEFAULT CTOR & PRIMARY MANIPULATORS" << endl
                          << "===================================" << endl;

        const char *D1 = INVALID_PATH;  // rootPath

        const char *A1 = "";

        const char *B1 = "a_" SUFFICIENTLY_LONG_STRING;

        if (verbose) cout << "\nTesting with various allocator configurations."
                          << endl;

        for (char cfg = 'a'; cfg <= 'd'; ++cfg) {
            bsls::AssertTestHandlerGuard hG;

            const char CONFIG = cfg;  // (how we specify the allocator)

            bslma::TestAllocator fa("footprint", veryVeryVeryVerbose);
            bslma::TestAllocator da("default",   veryVeryVeryVerbose);
            bslma::TestAllocator sa("supplied",  veryVeryVeryVerbose);

            bslma::DefaultAllocatorGuard dag(&da);

            Obj                  *objPtr          = 0;
            bslma::TestAllocator *objAllocatorPtr = 0;

            switch (CONFIG) {
              case 'a': {
                objPtr = new (fa) Obj();
                objAllocatorPtr = &da;
              } break;
              case 'b': {
                objPtr = new (fa) Obj(Obj::allocator_type());
                objAllocatorPtr = &da;
              } break;
              case 'c': {
                objPtr = new (fa) Obj(&sa);
                objAllocatorPtr = &sa;
              } break;
              case 'd': {
                objPtr = new (fa) Obj(Obj::allocator_type(&sa));
                objAllocatorPtr = &sa;
              } break;
              default: {
                LOOP_ASSERT(CONFIG, !"Bad allocator Config.");
              } break;
            }

            Obj& mX = *objPtr; const Obj& X = mX;
            bslma::TestAllocator&  oa = *objAllocatorPtr;
            bslma::TestAllocator& noa = &da == &oa ? sa : da;

            // -------------------------------------
            // Verify the object's attribute values.
            // -------------------------------------

            ASSERT_FAIL(X.rootPath());
            ASSERT_FAIL(X.isRootPathPlausible());

            LOOP3_ASSERT(CONFIG, &oa, ALLOC_OF(X), &oa == X.get_allocator());

            // Verify that no memory is allocate by from the non-object
            // allocator.

            LOOP_ASSERT(noa.numBlocksTotal(), 0 == noa.numBlocksTotal());

            // -----------------------------------------------------
            // Verify that each attribute is independently settable.
            // -----------------------------------------------------

            // rootPath
            {
                BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(sa){
                    mX.configureRootPath(A1);
                } BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
                LOOP_ASSERT(CONFIG, A1 == X.rootPath());

                mX.configureRootPath(B1);
                LOOP_ASSERT(CONFIG, B1 == X.rootPath());

                mX.configureRootPath(D1);
            }

            // Reclaim dynamically allocated object under test.

            fa.deleteObject(objPtr);

            // Verify that there is no longer any memory allocated.

            LOOP2_ASSERT(CONFIG, fa.numBlocksInUse(),
                         0 == fa.numBlocksInUse());
            LOOP2_ASSERT(CONFIG, oa.numBlocksInUse(),
                         0 == oa.numBlocksInUse());

            // Temporary string is created for comparison in assertion, so some
            // memory is allocated by default allocator, but it should be
            // released correctly at this point.

            LOOP2_ASSERT(CONFIG, noa.numBlocksInUse(),
                         0 == noa.numBlocksInUse());

            // Double check that at least some object memory got allocated.

            LOOP_ASSERT(CONFIG, 1 <= oa.numBlocksTotal());

            // Note that memory should be independently allocated for each
            // attribute capable of allocating memory.
        }
      } break;
      case 1: {
        // --------------------------------------------------------------------
        // BREATHING TEST:
        //   Developers' Sandbox.
        //
        // Plan:
        //   Perform and ad-hoc test of the primary modifiers and accessors.
        //
        // Testing:
        //   This "test" *exercises* basic functionality, but *tests* nothing.
        // --------------------------------------------------------------------

        if (verbose) cout << endl << "BREATHING TEST" << endl
                                  << "==============" << endl;

        {
            if (veryVerbose) {
                cout << "\tTesting 'construction'." << endl;
            }

            Obj mX(Z);
            ASSERT(0 != mX.configureRootPathIfPlausible("junk"));
            ASSERT(0 != mX.configureRootPathIfPlausible("."));
            bsl::string temp(Z);
            ASSERT(0 == defaultAllocator.numBytesInUse());
        }
        ASSERT(0 == defaultAllocator.numBytesInUse());
        {
            if (veryVerbose) {
                cout << "\tTesting 'loadTimeZoneFilePath' & 'rootPath'."
                     << endl;
            }
            Obj mX(Z); const Obj& X = mX;
            ASSERT(0 != mX.configureRootPathIfPlausible("."));
            mX.configureRootPath(".");
            ASSERT(!X.isRootPathPlausible());

            bsl::string path(Z);
            ASSERT(0 == X.loadTimeZoneFilePath(&path, "America/New_York"));
#ifndef BSLS_PLATFORM_OS_WINDOWS
            ASSERT(path == "./America/New_York");
#else
            ASSERT(path == ".\\America\\New_York");
#endif

            ASSERT(0 == X.loadTimeZoneFilePath(&path, "Pacific/Fiji"));
#ifndef BSLS_PLATFORM_OS_WINDOWS
            ASSERT(path == "./Pacific/Fiji");
#else
            ASSERT(path == ".\\Pacific\\Fiji");
#endif
            ASSERT(0 == defaultAllocator.numBytesInUse());
        }
        {
            if (veryVerbose) {
                cout << "\tTesting 'loadTimeZone'." << endl;
            }
            Obj mX(Z);
            mX.configureRootPath(TEST_DIRECTORY);

            bsl::string path(Z);
            baltzo::Zoneinfo tz(Z); const baltzo::Zoneinfo& TZ = tz;

            ASSERT(0 != mX.loadTimeZone(&tz, "FOO"));
            ASSERT(0 == mX.loadTimeZone(&tz, "America/New_York"));
            ASSERT("America/New_York" == TZ.identifier());

            if (veryVeryVerbose) {
                TZ.print(bsl::cout, 1, 3);
            }
        }

      } break;
      default: {
        cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl;
        testStatus = -1;
      }
    }

    if (testStatus > 0) {
        cerr << "Error, non-zero test status = " << testStatus << "." << endl;
    }

    // TBD: multiple test cases use the same path and so cleanup can not occur
    //      after each test case ends, or else there is a race condition when
    //      multiple test cases are run in parallel
    //bdls::FilesystemUtil::remove(TEST_DIRECTORY, true);

    return testStatus;
}

// ----------------------------------------------------------------------------
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
